#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 5;  // 最大节点数
int D, A[N], B[N];       // D:树的直径，A和B:存储两棵树中各节点的最远距离

vector<int> G[N]; // 存储树的邻接表

int dep[N], sumj[N];  // dep:存储节点深度，sumj:用于计算后缀和
ll suffix[N];         // suffix:后缀和数组

// 深度优先搜索，计算每个节点的深度
void dfs(int u, int fa) {
  for (int son : G[u])
    if (son != fa) {
      dep[son] = dep[u] + 1;
      dfs(son, u);
    }
}

// 解决函数，计算树的直径和各节点到最远节点的距离
void solve(int *d, int n) {
  // 清空邻接表
  for (int i = 1; i <= n; i++)
    G[i].clear();
  
  // 读取边信息，构建邻接表
  for (int i = 1; i < n; i++) {
    int u, v;
    scanf("%d %d", &u, &v);
    G[u].push_back(v);
    G[v].push_back(u);
  }

  // 第一次DFS，找到直径的一个端点
  dep[1] = 0; // 从节点1开始搜索
  dfs(1, 0);
  int u = 1;
  for (int i = 2; i <= n; i++) {
    if (dep[i] > dep[u])
      u = i; // u最终是直径的一个端点
  }

  // 第二次DFS，从u开始找到直径的另一个端点
  dep[u] = 0;
  dfs(u, 0); // u是直径的一个端点，从u开始搜索
  memcpy(d, dep, sizeof(int) * (n + 1));

  int v = 1;
  for (int i = 2; i <= n; i++) {
    if (dep[i] > dep[v])
      v = i; // v最终是直径的另外一个端点
  }

  D = max(D, dep[v]); // D是直径的长度
  dep[v] = 0;
  dfs(v, 0);
  
  // 计算每个节点到最远节点的距离
  for (int i = 1; i <= n; i++) {
    d[i] = max(d[i], dep[i]);
  }
}

int main() {
  int n1;
  scanf("%d", &n1);
  solve(A, n1);  // 处理第一棵树

  int n2;
  scanf("%d", &n2);
  solve(B, n2);  // 处理第二棵树

  // 计算B数组的后缀和
  for (int j = 1; j <= n2; j++) {
    sumj[B[j]]++;
    suffix[B[j]] += B[j];
  }
  
  // 计算后缀和
  for (int j = D - 1; j >= 0; j--) {  
    sumj[j] += sumj[j + 1];
    suffix[j] += suffix[j + 1];
  }
  
  // 还可以 把B数组排序，排序后二分查找，时间复杂度O(nlogn), 

  // 计算最终答案
  ll ans = 0;
  for (int i = 1; i <= n1; i++) {  // O(n)
    int cntj = sumj[D - A[i]];     // 满足条件的j的数量
    ll s = suffix[D - A[i]];       // 满足条件的j的B[j]之和
    ans += (ll)A[i] * cntj + s + cntj;  // 计算贡献
    ans += (ll)D * (n2 - cntj);    // 加上直径长度的贡献
  }

  printf("%lld\n", ans);
  return 0;
}